package studio.raptor.ddal.core.engine;

import java.io.Closeable;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import studio.raptor.ddal.common.collections.FastArrayList;
import studio.raptor.ddal.common.sql.SQLHintParser.SQLHint;
import studio.raptor.ddal.config.model.shard.Index;
import studio.raptor.ddal.config.model.shard.Table;
import studio.raptor.ddal.core.connection.BackendConnection;
import studio.raptor.ddal.core.engine.plan.PlanNodeChain;
import studio.raptor.ddal.core.executor.BatchExecutionUnit;
import studio.raptor.ddal.core.executor.ExecutionGroup;
import studio.raptor.ddal.core.executor.resultset.ResultData;
import studio.raptor.ddal.core.merger.ResultDataMergeContext;
import studio.raptor.ddal.core.parser.builder.SqlBuilder;
import studio.raptor.ddal.core.parser.result.ParseResult;
import studio.raptor.ddal.core.parser.result.SQLStatementType;
import studio.raptor.ddal.core.router.result.RouteResult;
import studio.raptor.ddal.core.router.util.RouteCondition;
import studio.raptor.sqlparser.stat.TableStat.Condition;

/**
 * 功能描述
 *
 * @author Charley
 * @since 1.0
 */
public class StatementContext implements Closeable{
  //sql相关
  private String originSql;
  private SQLHint sqlHint;
  private boolean hasHint;
  private String sqlHintStr;
  private String trimmedHintSql; // 不包含注释的SQL
  private boolean isPlanPersistable = false; // 是否要持久化执行计划
  private PlanNodeChain planInstance = null; // 执行计划
  private boolean isPreparedStatement;
  private List<Object> sqlParameters = new FastArrayList<>();

  private boolean isAddBatch =false;//是否为批处理
  private boolean isExecuteBatch =false;//是否为批处理提交动作
  private ArrayList<String> batchSqlList= new ArrayList<String>(  );//批处理动态sql列表(分表模式)
  protected Map<String,BatchExecutionUnit> batchExecutionUnitMap=new HashMap<String,BatchExecutionUnit>( );//批处理时不同分片上的批处理辅助执行单元
  protected Map<String,BackendConnection> backendConnectionMap=new HashMap<String,BackendConnection>( );//批处理时持有后端物理链接

  private List<Table> shardTables = new ArrayList<>();
  private List<RouteCondition> routeConditions = new FastArrayList<>();
  private ExecutionGroup currentExecutionGroup;
  private ResultDataMergeContext resultDataMergeContext;
  private SqlBuilder sqlBuilder;

  private ParseResult parseResult;
  private List<Condition> hintShardConditions = new FastArrayList<>();
  private RouteResult routeResult;
  private List<ResultData> resultDataList;
  private ResultData mergedResult;
  //index
  private List<IndexTableOps> indexTableOpsList = new FastArrayList<>();
  private Map<String, Index> indexTables;

  //Log对象
  private SqlProcessLog sqlLog = new SqlProcessLog(this);

  public boolean isAddBatch() {
    return isAddBatch;
  }

  public void setAddBatch(boolean addBatch) {
    isAddBatch = addBatch;
  }

  public boolean isExecuteBatch() {
    return isExecuteBatch;
  }

  public void setExecuteBatch(boolean executeBatch) {
    isExecuteBatch = executeBatch;
  }

  public ArrayList<String> getBatchSqlList() {
    return batchSqlList;
  }

  public void setBatchSqlList(ArrayList<String> batchSqlList) {
    this.batchSqlList = batchSqlList;
  }

  public String getOriginSql() {
    return originSql;
  }

  public void setOriginSql(String originSql) {
    this.originSql = originSql;
  }

  public SQLHint getSqlHint() {
    return sqlHint;
  }

  public void setSqlHint(SQLHint sqlHint) {
    this.sqlHint = sqlHint;
  }

  public boolean hasHint() {
    return hasHint;
  }

  public void setHasHint(boolean hasHint) {
    this.hasHint = hasHint;
  }

  public String getSqlHintStr() {
    return sqlHintStr;
  }

  public void setSqlHintStr(String sqlHintStr) {
    this.sqlHintStr = sqlHintStr;
  }

  public String getTrimmedHintSql() {
    return trimmedHintSql;
  }

  public void setTrimmedHintSql(String trimmedHintSql) {
    this.trimmedHintSql = trimmedHintSql;
  }

  public boolean isPlanPersistable() {
    return isPlanPersistable;
  }

  public void setPlanPersistable(boolean planPersistable) {
    isPlanPersistable = planPersistable;
  }

  public PlanNodeChain getPlanInstance() {
    return planInstance;
  }

  public void setPlanInstance(PlanNodeChain planInstance) {
    this.planInstance = planInstance;
  }

  public boolean isPreparedStatement() {
    return isPreparedStatement;
  }

  public void setPreparedStatement(boolean preparedStatement) {
    isPreparedStatement = preparedStatement;
  }

  public List<Object> getSqlParameters() {
    return sqlParameters;
  }

  public void setSqlParameters(List<Object> sqlParameters) {
    this.sqlParameters = sqlParameters;
  }

  public List<Table> getShardTables() {
    return shardTables;
  }

  public void setShardTables(List<Table> shardTables) {
    this.shardTables = shardTables;
  }

  public List<RouteCondition> getRouteConditions() {
    return routeConditions;
  }

  public void setRouteConditions(
      List<RouteCondition> routeConditions) {
    this.routeConditions = routeConditions;
  }

  public ExecutionGroup getCurrentExecutionGroup() {
    return currentExecutionGroup;
  }

  public void setCurrentExecutionGroup(
      ExecutionGroup currentExecutionGroup) {
    this.currentExecutionGroup = currentExecutionGroup;
  }

  public ResultDataMergeContext getResultDataMergeContext() {
    return resultDataMergeContext;
  }

  public void setResultDataMergeContext(
      ResultDataMergeContext resultDataMergeContext) {
    this.resultDataMergeContext = resultDataMergeContext;
  }

  public SqlBuilder getSqlBuilder() {
    return sqlBuilder;
  }

  public void setSqlBuilder(SqlBuilder sqlBuilder) {
    this.sqlBuilder = sqlBuilder;
  }

  public ParseResult getParseResult() {
    return parseResult;
  }

  public void setParseResult(ParseResult parseResult) {
    this.parseResult = parseResult;
  }

  public List<Condition> getHintShardConditions() {
    return hintShardConditions;
  }

  public void setHintShardConditions(
      List<Condition> hintShardConditions) {
    this.hintShardConditions = hintShardConditions;
  }

  public Condition findHintShardCondition(String column){
    for (Condition condition : hintShardConditions){
      if(condition.getColumn().getName().equalsIgnoreCase(column)){
        return condition;
      }
    }
    return null;
  }

  public RouteResult getRouteResult() {
    return routeResult;
  }

  public void setRouteResult(RouteResult routeResult) {
    this.routeResult = routeResult;
  }

  public List<ResultData> getResultDataList() {
    return resultDataList;
  }

  public void setResultDataList(
      List<ResultData> resultDataList) {
    this.resultDataList = resultDataList;
  }

  public ResultData getMergedResult() {
    return mergedResult;
  }

  public void setMergedResult(ResultData mergedResult) {
    this.mergedResult = mergedResult;
  }

  public List<IndexTableOps> getIndexTableOpsList() {
    return indexTableOpsList;
  }

  public void setIndexTableOpsList(
      List<IndexTableOps> indexTableOpsList) {
    this.indexTableOpsList = indexTableOpsList;
  }

  /**
   * 获取当前执行SQL的类型。
   *
   * @return 被执行SQL的类型。
   */
  public SQLStatementType getSqlStatementType() {
    return this.parseResult.getSqlType();
  }

  /**
   * Closes this stream and releases any system resources associated
   * with it. If the stream is already closed then invoking this
   * method has no effect.
   *
   * <p> As noted in {@link AutoCloseable#close()}, cases where the
   * close may fail require careful attention. It is strongly advised
   * to relinquish the underlying resources and to internally
   * <em>mark</em> the {@code Closeable} as closed, prior to throwing
   * the {@code IOException}.
   *
   * @throws IOException if an I/O error occurs
   */
  @Override
  public void close() throws IOException {
    sqlLog.print();
    clear();
  }

  public Map<String, BatchExecutionUnit> getBatchExecutionUnitMap() {
    return batchExecutionUnitMap;
  }

  public void setBatchExecutionUnitMap(Map<String, BatchExecutionUnit> batchExecutionUnitMap) {
    this.batchExecutionUnitMap = batchExecutionUnitMap;
  }

  public Map getBackendConnectionMap() {
    return backendConnectionMap;
  }

  public void setBackendConnectionMap(Map backendConnectionMap) {
    this.backendConnectionMap = backendConnectionMap;
  }

  public SqlProcessLog getSqlLog() {
    return sqlLog;
  }

  public void setSqlLog(SqlProcessLog sqlLog) {
    this.sqlLog = sqlLog;
  }

  public Map<String, Index> getIndexTables() {
    return indexTables;
  }

  public void setIndexTables(Map<String, Index> indexTables) {
    this.indexTables = indexTables;
  }

  public void clear() {

    // 清理SQL注释解析结果
    sqlHint = null;
    sqlHintStr = null;
    // 清理执行组
    currentExecutionGroup = null;

    // clear index table ops
    try {
      indexTableOpsList.clear();
    } catch (Exception ignore) {
      //no-ops
    }


    try {
      sqlParameters.clear();
    } catch (Exception ignore) {
      // no-ops
    }

    try {
      hintShardConditions.clear();
    } catch (Exception ignore) {
      // no-ops
    }

    try {
      routeConditions.clear();
    } catch (Exception ignore) {
      // no-ops
    }

    try {
      shardTables.clear();
    } catch (Exception ignore) {
      // no-ops
    }

    try {
      resultDataList.clear();
    } catch (Exception ignore) {
      // no-ops
    }
  }
}